{/* Copyright 2020 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License. */}

import {Layout} from '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-aria/listbox';
import collectionsDocs from 'docs:@react-types/shared/src/collections.d.ts';
import selectionDocs from 'docs:@react-stately/selection';
import statelyDocs from 'docs:@react-stately/list';
import utilsDocs from 'docs:@react-aria/utils';
import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-aria/listbox/package.json';
import Anatomy from './anatomy.svg';
import {Keyboard} from '@react-spectrum/text';
import ChevronRight from '@spectrum-icons/workflow/ChevronRight';

---
category: Collections
keywords: [listbox, aria]
---

# useListBox

<PageDescription>{docs.exports.useListBox.description}</PageDescription>

<HeaderInfo
  packageData={packageData}
  componentNames={['useListBox', 'useOption', 'useListBoxSection']}
  sourceData={[
    {type: 'W3C', url: 'https://www.w3.org/WAI/ARIA/apg/patterns/listbox/'}
  ]} />

## API

<FunctionAPI function={docs.exports.useListBox} links={docs.links} />
<FunctionAPI function={docs.exports.useOption} links={docs.links} />
<FunctionAPI function={docs.exports.useListBoxSection} links={docs.links} />

## Features

A listbox can be built using the [&lt;select&gt;](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)
and [&lt;option&gt;](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option) HTML elements, but this is
not possible to style consistently cross browser. `useListBox` helps achieve accessible
listbox components that can be styled as needed.

Note: `useListBox` only handles the list itself. For a dropdown similar to a `<select>`, see [useSelect](../Select/useSelect.html).

* Exposed to assistive technology as a `listbox` using ARIA
* Support for single, multiple, or no selection
* Support for disabled items
* Support for sections
* Labeling support for accessibility
* Support for mouse, touch, and keyboard interactions
* Tab stop focus management
* Keyboard navigation support including arrow keys, home/end, page up/down, select all, and clear
* Automatic scrolling support during keyboard navigation
* Typeahead to allow focusing options by typing text
* Support for use with virtualized lists

## Anatomy

<Anatomy />

A listbox consists of a container element, with a list of options or groups inside.
`useListBox`, `useOption`, and `useListBoxSection` handle exposing this to assistive
technology using ARIA, along with handling keyboard, mouse, and interactions to support
selection and focus behavior.

`useListBox` returns props that you should spread onto the list container element,
along with props for an optional visual label:

<TypeContext.Provider value={docs.links}>
  <InterfaceType properties={docs.links[docs.exports.useListBox.return.id].properties} />
</TypeContext.Provider>

`useOption` returns props for an individual option and its children, along with states you can use for styling:

<TypeContext.Provider value={docs.links}>
  <InterfaceType properties={docs.links[docs.exports.useOption.return.id].properties} />
</TypeContext.Provider>

`useListBoxSection` returns props for a section:

<TypeContext.Provider value={docs.links}>
  <InterfaceType properties={docs.links[docs.exports.useListBoxSection.return.id].properties} />
</TypeContext.Provider>

State is managed by the <TypeLink links={statelyDocs.links} type={statelyDocs.exports.useListState} />
hook from `@react-stately/list`. The state object should be passed as an option to
each of the above hooks.

If a listbox, options, or group does not have a visible label, an `aria-label` or `aria-labelledby`
prop must be passed instead to identify the element to assistive technology.

## State management

`useListBox` requires knowledge of the options in the listbox in order to handle keyboard
navigation and other interactions. It does this using
the <TypeLink links={collectionsDocs.links} type={collectionsDocs.exports.Collection} />
interface, which is a generic interface to access sequential unique keyed data. You can
implement this interface yourself, e.g. by using a prop to pass a list of item objects,
but <TypeLink links={statelyDocs.links} type={statelyDocs.exports.useListState} /> from
`@react-stately/list` implements a JSX based interface for building collections instead.
See [Collection Components](v3:collections.html) for more information.

In addition, <TypeLink links={statelyDocs.links} type={statelyDocs.exports.useListState} />
manages the state necessary for multiple selection and exposes
a <TypeLink links={selectionDocs.links} type={selectionDocs.exports.SelectionManager} />,
which makes use of the collection to provide an interface to update the selection state.
For more information, see [Selection](v3:selection.html).

## Example

This example uses HTML `<ul>` and `<li>` elements to represent the list, and applies
props from <TypeLink links={docs.links} type={docs.exports.useListBox} />
and <TypeLink links={docs.links} type={docs.exports.useOption} />.
For each item in the collection in state, either an `Option` or `ListBoxSection` (defined [below](#sections))
is rendered according to the item's `type` property.

```tsx example export=true
import type {AriaListBoxProps} from '@react-aria/listbox';
import {useListBox, useOption} from '@react-aria/listbox';
import {useListState} from '@react-stately/list';
import {Item} from '@react-stately/collections';
import {useFocusRing} from '@react-aria/focus';
import {mergeProps} from '@react-aria/utils';

function ListBox<T extends object>(props: AriaListBoxProps<T>) {
  // Create state based on the incoming props
  let state = useListState(props);

  // Get props for the listbox element
  let ref = React.useRef(null);
  let {listBoxProps, labelProps} = useListBox(props, state, ref);

  return (
    <>
      <div {...labelProps}>{props.label}</div>
      <ul {...listBoxProps} ref={ref}>
        {[...state.collection].map(item => (
          item.type === 'section'
            ? <ListBoxSection key={item.key} section={item} state={state} />
            : <Option key={item.key} item={item} state={state} />
        ))}
      </ul>
    </>
  );
}

function Option({item, state}) {
  // Get props for the option element
  let ref = React.useRef(null);
  let {optionProps} = useOption({key: item.key}, state, ref);

  // Determine whether we should show a keyboard
  // focus ring for accessibility
  let {isFocusVisible, focusProps} = useFocusRing();

  return (
    <li
      {...mergeProps(optionProps, focusProps)}
      ref={ref}
      data-focus-visible={isFocusVisible}>
      {item.rendered}
    </li>
  );
}

<ListBox label="Alignment" selectionMode="single">
  <Item>Left</Item>
  <Item>Middle</Item>
  <Item>Right</Item>
</ListBox>
```

<details>
  <summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show CSS</summary>

```css
[role=listbox] {
  padding: 0;
  margin: 5px 0;
  list-style: none;
  border: 1px solid gray;
  max-width: 250px;
  max-height: 300px;
  overflow: auto;
}

[role=option] {
  display: block;
  padding: 2px 5px;
  outline: none;
  cursor: default;
  color: inherit;

  &[data-focus-visible=true] {
    outline: 2px solid orange;
  }

  &[aria-selected=true] {
    background: blueviolet;
    color: white;
  }

  &[aria-disabled] {
    color: #aaa;
  }
}
```

</details>

## Dynamic collections

`ListBox` follows the [Collection Components API](v3:collections.html), accepting both static and dynamic collections.
The example above shows static collections, which can be used when the full list of options is known ahead of time. Dynamic collections,
as shown below, can be used when the options come from an external data source such as an API call, or update over time.

As seen below, an iterable list of options is passed to the ListBox using the `items` prop. Each item accepts a `key` prop, which
is passed to the `onSelectionChange` handler to identify the selected item. Alternatively, if the item objects contain an `id` property,
as shown in the example below, then this is used automatically and a `key` prop is not required.

```tsx example
function Example() {
  let options = [
    { id: 1, name: 'Aardvark' },
    { id: 2, name: 'Cat' },
    { id: 3, name: 'Dog' },
    { id: 4, name: 'Kangaroo' },
    { id: 5, name: 'Koala' },
    { id: 6, name: 'Penguin' },
    { id: 7, name: 'Snake' },
    { id: 8, name: 'Turtle' },
    { id: 9, name: 'Wombat' }
  ];

  return (
    <ListBox label="Animals" items={options} selectionMode="single">
      {(item) => <Item>{item.name}</Item>}
    </ListBox>
  );
}
```

## Selection

ListBox supports multiple selection modes. By default, selection is disabled, however this can be changed using the `selectionMode` prop.
Use `defaultSelectedKeys` to provide a default set of selected items (uncontrolled) and `selectedKeys` to set the selected items (controlled). The value of the selected keys must match the `key` prop of the items.
See the `react-stately` [Selection docs](v3:selection.html) for more details.

```tsx example
import type {Selection} from 'react-stately';

function Example() {
  let [selected, setSelected] = React.useState<Selection>(new Set(['cheese']));

  return (
    <>
      <ListBox label="Choose sandwich contents" selectionMode="multiple" selectedKeys={selected} onSelectionChange={setSelected}>
        <Item key="lettuce">Lettuce</Item>
        <Item key="tomato">Tomato</Item>
        <Item key="cheese">Cheese</Item>
        <Item key="tuna">Tuna Salad</Item>
        <Item key="egg">Egg Salad</Item>
        <Item key="ham">Ham</Item>
      </ListBox>
      <p>Current selection (controlled): {selected === 'all' ? 'all' : [...selected].join(', ')}</p>
    </>
  );
}
```

### Selection behavior

By default, `useListBox` uses the `"toggle"` selection behavior, which behaves like a checkbox group: clicking, tapping, or pressing the <Keyboard>Space</Keyboard> or <Keyboard>Enter</Keyboard> keys toggles selection for the focused row. Using the arrow keys moves focus but does not change selection.

When `selectionBehavior` is set to `"replace"`, clicking a row with the mouse replaces the selection with only that row. Using the arrow keys moves both focus and selection. To select multiple rows, modifier keys such as <Keyboard>Ctrl</Keyboard>, <Keyboard>Cmd</Keyboard>, and <Keyboard>Shift</Keyboard> can be used. On touch screen devices, selection always behaves as toggle since modifier keys may not be available.

These selection behaviors are defined in [Aria Practices](https://www.w3.org/WAI/ARIA/apg/patterns/listbox/#keyboardinteraction).

```tsx example
<ListBox label="Choose sandwich contents" selectionMode="multiple" selectionBehavior="replace">
  <Item key="lettuce">Lettuce</Item>
  <Item key="tomato">Tomato</Item>
  <Item key="cheese">Cheese</Item>
  <Item key="tuna">Tuna Salad</Item>
  <Item key="egg">Egg Salad</Item>
  <Item key="ham">Ham</Item>
</ListBox>
```

## Sections

ListBox supports sections with separators and headings in order to group options. Sections can be used by wrapping groups of Items in a `Section` component. Each `Section` takes a `title` and `key` prop.
To implement sections, implement the `ListBoxSection` component referenced above
using the <TypeLink links={docs.links} type={docs.exports.useListBoxSection} /> hook. It will include four extra elements:
an `<li>` between the sections to represent the separator, an `<li>` to contain the heading `<span>` element, and a
`<ul>` to contain the child items. This structure is necessary to ensure HTML semantics
are correct.

```tsx example export=true render=false
import {useListBoxSection} from '@react-aria/listbox';

function ListBoxSection({section, state}) {
  let {itemProps, headingProps, groupProps} = useListBoxSection({
    heading: section.rendered,
    'aria-label': section['aria-label']
  });

  // If the section is not the first, add a separator element to provide visual separation.
  // The heading is rendered inside an <li> element, which contains
  // a <ul> with the child items.
  return <>
    {section.key !== state.collection.getFirstKey() &&
      <li
        role="presentation"
        style={{
          borderTop: '1px solid gray',
          margin: '2px 5px'
        }} />
    }
    <li {...itemProps}>
      {section.rendered &&
        <span
          {...headingProps}
          style={{
            fontWeight: 'bold',
            fontSize: '1.1em',
            padding: '2px 5px',
          }}>
          {section.rendered}
        </span>
      }
      <ul
        {...groupProps}
        style={{
          padding: 0,
          listStyle: 'none'
        }}>
        {[...section.childNodes].map(node =>
          <Option
            key={node.key}
            item={node}
            state={state} />
        )}
      </ul>
    </li>
  </>;
}
```

### Static items

With this in place, we can now render a static ListBox with multiple sections:

```tsx example
import {Section} from '@react-stately/collections';

<ListBox label="Choose sandwich contents" selectionMode="multiple">
  <Section title="Veggies">
    <Item key="lettuce">Lettuce</Item>
    <Item key="tomato">Tomato</Item>
    <Item key="onion">Onion</Item>
  </Section>
  <Section title="Protein">
    <Item key="ham">Ham</Item>
    <Item key="tuna">Tuna</Item>
    <Item key="tofu">Tofu</Item>
  </Section>
  <Section title="Condiments">
    <Item key="mayo">Mayonaise</Item>
    <Item key="mustard">Mustard</Item>
    <Item key="ranch">Ranch</Item>
  </Section>
</ListBox>
```

### Dynamic items

The above example shows sections with static items. Sections can also be populated from a hierarchical data structure.
Similarly to the props on ListBox, `<Section>` takes an array of data using the `items` prop.

```tsx example
import type {Selection} from 'react-stately';

function Example() {
  let options = [
    {name: 'Australian', children: [
      {id: 2, name: 'Koala'},
      {id: 3, name: 'Kangaroo'},
      {id: 4, name: 'Platypus'}
    ]},
    {name: 'American', children: [
      {id: 6, name: 'Bald Eagle'},
      {id: 7, name: 'Bison'},
      {id: 8, name: 'Skunk'}
    ]}
  ];
  let [selected, setSelected] = React.useState<Selection>(new Set());

  return (
    <ListBox
      label="Pick an animal"
      items={options}
      selectedKeys={selected}
      selectionMode="single"
      onSelectionChange={setSelected}>
      {item => (
        <Section key={item.name} items={item.children} title={item.name}>
          {item => <Item>{item.name}</Item>}
        </Section>
      )}
    </ListBox>
  );
}

```

### Accessibility

Sections without a `title` must provide an `aria-label` for accessibility.

## Complex options

By default, options that only contain text will be labeled by the contents of the option.
For options that have more complex content (e.g. icons, multiple lines of text, etc.), use
`labelProps` and `descriptionProps` from <TypeLink links={docs.links} type={docs.exports.useOption} />
as needed to apply to the main text element of the option and its description. This improves screen
reader announcement.

**NOTE: listbox options cannot contain interactive content (e.g. buttons, checkboxes, etc.).
For these cases, see [useGridList](../GridList/useGridList.html) instead.**

To implement this, we'll update the `Option` component to apply the ARIA properties
returned by <TypeLink links={docs.links} type={docs.exports.useOption} /> to the appropriate
elements. In this example, we'll pull them out of `props.children` and use `React.cloneElement`
to apply the props, but you may want to use a more robust approach (e.g. context).

```tsx example
///- begin collapse -///
function ListBox(props) {
  let state = useListState(props);
  let ref = React.useRef(null);
  let {listBoxProps, labelProps} = useListBox(props, state, ref);

  return (
    <>
      <div {...labelProps}>{props.label}</div>
      <ul {...listBoxProps} ref={ref}>
        {[...state.collection].map(item => (
          <Option
            key={item.key}
            item={item}
            state={state} />
        ))}
      </ul>
    </>
  );
}
///- end collapse -///
function Option({item, state}) {
  let ref = React.useRef(null);
  let {optionProps, labelProps, descriptionProps} = useOption({key: item.key}, state, ref);
  let {isFocusVisible, focusProps} = useFocusRing();

  // Pull out the two expected children. We will clone them
  // and add the necessary props for accessibility.
  let [title, description] = item.rendered;

  return (
    <li
      {...mergeProps(optionProps, focusProps)}
      ref={ref}
      data-focus-visible={isFocusVisible}>
      {React.cloneElement(title, labelProps)}
      {React.cloneElement(description, descriptionProps)}
    </li>
  );
}

<ListBox label="Text alignment" selectionMode="single">
  <Item textValue="Align Left">
    <div><strong>Align Left</strong></div>
    <div>Align the selected text to the left</div>
  </Item>
  <Item textValue="Align Center">
    <div><strong>Align Center</strong></div>
    <div>Align the selected text center</div>
  </Item>
  <Item textValue="Align Right">
    <div><strong>Align Right</strong></div>
    <div>Align the selected text to the right</div>
  </Item>
</ListBox>
```

## Asynchronous loading

This example uses the [useAsyncList](../useAsyncList.html) hook to handle asynchronous loading
of data from a server. You may additionally want to display a spinner to indicate the loading
state to the user, or support features like infinite scroll to load more data.

```tsx example
import {useAsyncList} from '@react-stately/data';

interface Pokemon {
  name: string
}

function AsyncLoadingExample() {
  let list = useAsyncList<Pokemon>({
    async load({signal}) {
      let res = await fetch(
        `https://pokeapi.co/api/v2/pokemon`,
        {signal}
      );
      let json = await res.json();

      return {
        items: json.results
      };
    }
  });

  return (
    <ListBox label="Pick a Pokemon" items={list.items} selectionMode="single">
      {(item) => <Item key={item.name}>{item.name}</Item>}
    </ListBox>
  );
}
```

## Links

By default, interacting with an item in a ListBox triggers `onSelectionChange`. Alternatively, items may be links to another page or website. This can be achieved by passing the `href` prop to the `<Item>` component.

This example shows how to update the `Option` component with support for rendering an `<a>` element if an `href` prop is passed to the item. Note that you'll also need to render the `ListBox` as a `<div>` instead of a `<ul>`, since an `<a>` inside a `<ul>` is not valid HTML.

```tsx example
///- begin collapse -///
function ListBox(props) {
  let state = useListState(props);
  let ref = React.useRef(null);
  let {listBoxProps, labelProps} = useListBox(props, state, ref);

  return (
    <>
      <div {...labelProps}>{props.label}</div>
      <ul {...listBoxProps} ref={ref}>
        {[...state.collection].map(item => (
          <Option
            key={item.key}
            item={item}
            state={state} />
        ))}
      </ul>
    </>
  );
}
///- end collapse -///
function Option({item, state}) {
  let ref = React.useRef(null);
  let {optionProps} = useOption({key: item.key}, state, ref);
  let {isFocusVisible, focusProps} = useFocusRing();
  /*- begin highlight -*/
  let ElementType: React.ElementType = item.props.href ? 'a' : 'div';
  /*- end highlight -*/

  return (
    <ElementType
      {...mergeProps(optionProps, focusProps)}
      ref={ref}
      data-focus-visible={isFocusVisible}>
      {item.rendered}
    </ElementType>
  );
}

<ListBox aria-label="Links">
  <Item href="https://adobe.com/" target="_blank">Adobe</Item>
  <Item href="https://apple.com/" target="_blank">Apple</Item>
  <Item href="https://google.com/" target="_blank">Google</Item>
  <Item href="https://microsoft.com/" target="_blank">Microsoft</Item>
</ListBox>
```

```css hidden
[role=option][href] {
  text-decoration: none;
  cursor: pointer;
  -webkit-touch-callout: none;
}
```

By default, link items in a ListBox are not selectable, and only perform navigation when the user interacts with them. However, with the "replace" [selection behavior](#selection-behavior), items will be selected when single clicking or pressing the <Keyboard>Space</Keyboard> key, and navigate to the link when double clicking or pressing the <Keyboard>Enter</Keyboard> key.

```tsx example
///- begin collapse -///
function ListBox(props) {
  let state = useListState(props);
  let ref = React.useRef(null);
  let {listBoxProps, labelProps} = useListBox(props, state, ref);

  return (
    <>
      <div {...labelProps}>{props.label}</div>
      <ul {...listBoxProps} ref={ref}>
        {[...state.collection].map(item => (
          <Option
            key={item.key}
            item={item}
            state={state} />
        ))}
      </ul>
    </>
  );
}
function Option({item, state}) {
  let ref = React.useRef(null);
  let {optionProps} = useOption({key: item.key}, state, ref);
  let {isFocusVisible, focusProps} = useFocusRing();
  let ElementType: React.ElementType = item.props.href ? 'a' : 'div';

  return (
    <ElementType
      {...mergeProps(optionProps, focusProps)}
      ref={ref}
      data-focus-visible={isFocusVisible}>
      {item.rendered}
    </ElementType>
  );
}
///- end collapse -///

<ListBox aria-label="Links" selectionMode="multiple" selectionBehavior="replace">
  <Item href="https://adobe.com/" target="_blank">Adobe</Item>
  <Item href="https://apple.com/" target="_blank">Apple</Item>
  <Item href="https://google.com/" target="_blank">Google</Item>
  <Item href="https://microsoft.com/" target="_blank">Microsoft</Item>
</ListBox>
```

### Client side routing

The `<Item>` component works with frameworks and client side routers like [Next.js](https://nextjs.org/) and [React Router](https://reactrouter.com/en/main). As with other React Aria components that support links, this works via the <TypeLink links={utilsDocs.links} type={utilsDocs.exports.RouterProvider} /> component at the root of your app. See the [framework setup guide](../frameworks) to learn how to set this up.

## Disabled items

`useListBox` supports marking items as disabled using the `disabledKeys` prop. Each key in this list
corresponds with the `key` prop passed to the `Item` component, or automatically derived from the values passed
to the `items` prop. See [Collections](v3:collections.html) for more details.

Disabled items are not focusable, selectable, or keyboard navigable. The `isDisabled` property returned by
`useOption` can be used to style the item appropriately.

```tsx example
<ListBox label="Choose sandwich contents" selectionMode="multiple" disabledKeys={['tuna']}>
  <Item key="lettuce">Lettuce</Item>
  <Item key="tomato">Tomato</Item>
  <Item key="cheese">Cheese</Item>
  <Item key="tuna">Tuna Salad</Item>
  <Item key="egg">Egg Salad</Item>
  <Item key="ham">Ham</Item>
</ListBox>
```

## Internationalization

`useListBox` handles some aspects of internationalization automatically.
For example, type to select is implemented with an
[Intl.Collator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator)
for internationalized string matching. You are responsible for localizing all labels and option
content that is passed into the listbox.

### RTL

In right-to-left languages, the listbox options should be mirrored. The text content should be
aligned to the right. Ensure that your CSS accounts for this.
